#pragma once

#include <elasticlient/client.h>
#include <cpr/cpr.h>
#include <json/json.h>
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include "logger.hpp"

namespace xu
{
    using ClientPtr = std::shared_ptr<elasticlient::Client>;

    bool Serialize(const Json::Value &val, std::string &dst)
    {
        // 先定义Json::StreamWriter 工厂类 Json::StreamWriterBuilder
        Json::StreamWriterBuilder swb;
        swb.settings_["emitUTF8"] = true;
        std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
        // 通过Json::StreamWriter中的write接口进行序列化
        std::stringstream ss;
        int ret = sw->write(val, &ss);
        if (ret != 0)
        {
            LOG_ERROR("Json序列化失败!");
            return false;
        }
        dst = ss.str();
        return true;
    }
    bool UnSerialize(const std::string &src, Json::Value &val)
    {
        Json::CharReaderBuilder crb;
        std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
        std::string err;
        bool ret = cr->parse(src.c_str(), src.c_str() + src.size(), &val, &err);
        if (ret == false)
        {
            LOG_ERROR("Json反序列化失败!");
            return false;
        }
        return true;
    }


    class ESIndex
    {
    public:
        ESIndex(ClientPtr client,const std::string& index_name ,const std::string & type = "_doc")
        :_index_name(index_name),
        _type(type),
        _client(client)
        {
            Json::Value ik;
            Json::Value analyzer;
            Json::Value analysis;
            Json::Value settings;
            Json::Value tokenizer;
            tokenizer["tokenizer"] = "ik_max_word";
            ik["ik"] =  tokenizer;
            analyzer["analyzer"] = ik;
            analysis["analysis"] = analyzer;
            _index["settings"] = analysis;
        }

        ESIndex & Append(const std::string & key,const std::string& type = "text",
            const std::string &analyzer = "ik_max_word", bool enabled = true)
            {
                Json::Value val;
                val["type"] = type;
                val["analyzer"]  = analyzer;
                if(enabled == false)
                {
                    val["enabled"] = enabled;
                }

                _properties[key] = val; 
                return *this;
            }
        bool Create(const std::string& id)
        {
            /*cpr::Response index(const std::string &indexName, const std::string &docType,
            const std::string &id,const std::string &body,const std::string &routing = std::string())*/
            _mappings["dynamic"] = true;
            _mappings["properties"] = _properties;
            _index["mappings"] = _mappings;

            std::string body;
            if(Serialize(_index,body) == false)
            {
                return false;
            }

            LOG_DEBUG("序列化结果：{}",body);
            try
            {
                cpr::Response rsp = _client->index(_index_name,_type,id,body);
                if(rsp.status_code < 200 || rsp.status_code >= 400)
                {
                    LOG_ERROR("构建索引失败：{}",rsp.status_code);
                    return false;
                }
            }
            catch(std::exception &e) 
            {
                LOG_ERROR("触发异常，构建索引失败");
                return false;
            }

            return true;
        }
    private:
        std::string _index_name;
        std::string _type;
        ClientPtr   _client;
        Json::Value _index;
        Json::Value _mappings;
        Json::Value _properties;
    };
    class ESInsert
    {
    public:
        ESInsert(ClientPtr client,const std::string& index_name ,const std::string & type = "_doc")
         :_index_name(index_name),
        _type(type),
        _client(client){}

        template<typename T>
        ESInsert & Append(const std::string &key,const T &val)
        {
            _value[key] = val;
            return *this;
        }


        bool Insert(const std::string & id)
        {
            std::string body;
            if(Serialize(_value,body) == false)
            {
                return false;
            }

            LOG_DEBUG("序列化结果：{}",body);
            try
            {
                cpr::Response rsp = _client->index(_index_name,_type,id,body);
                if(rsp.status_code < 200 || rsp.status_code >= 400)
                {
                    LOG_ERROR("插入数据失败：{}",rsp.status_code);
                    return false;
                }
            }
            catch(std::exception &e) 
            {
                LOG_ERROR("触发异常，插入失败");
                return false;
            }

            return true;
        }
    private:
        std::string _index_name;
        std::string _type;
        ClientPtr _client;
        Json::Value _value;
    };
    class ESRemove
    {
    public:
        ESRemove(ClientPtr client,const std::string& index_name ,const std::string & type = "_doc")
         :_index_name(index_name),
        _type(type),
        _client(client){}
        bool Remove(const std::string & id)
        {
            try
            {
                cpr::Response rsp = _client->remove(_index_name,_type,id);
                if(rsp.status_code < 200 || rsp.status_code >= 400)
                {
                    LOG_ERROR("删除数据失败：{}",rsp.status_code);
                    return false;
                }
            }
            catch(std::exception &e) 
            {
                LOG_ERROR("触发异常，删除失败");
                return false;
            }

            return true;
        }
    private:
        std::string _index_name; 
        std::string _type;
        ClientPtr _client;
    };

    class ESSearch
    {
    public:
        ESSearch(ClientPtr client,const std::string& index_name ,const std::string & type = "_doc")
         :_index_name(index_name),
        _type(type),
        _client(client){}
        ESSearch& AppendMustNot(const std::string & key, const std::vector<std::string> & vals)
        {
            Json::Value value;
            for(auto &e : vals)
            {
                value[key].append(e);
            }
            Json::Value terms;
            terms["terms"] = value;
            _must_not.append(terms);

            return *this;
        }
        ESSearch& AppendShould(const std::string & key, const std::string & val)
        {
            Json::Value value;
            value[key] = val;
            Json::Value match;
            match["match"] = value;
            _should.append(match);

            return *this;
        }

        ESSearch& AppendMustTerm(const std::string &key, const std::string &val) 
        {
            Json::Value field;
            field[key] = val;
            Json::Value term;
            term["term"] = field;
            _must.append(term);
            return *this;
        }
        ESSearch& AppendMustMatch(const std::string &key, const std::string &val)
        {
            Json::Value field;
            field[key] = val;
            Json::Value match;
            match["match"] = field;
            _must.append(match);
            return *this;
        }
        Json::Value Search()
        {
            Json::Value value;
            if(_must_not.empty() == false)
            {
                value["must_not"] = _must_not;
            }
            if(_should.empty() == false)
            {
                value["should"] = _should;
            }
            if(_must.empty() == false)
            {
                value["must"] = _must;
            }

            Json::Value _bool;
            _bool["bool"] = value;
            Json::Value query;
            query["query"] = _bool;

 
            std::string body;
            if(Serialize( query,body) == false)
            {
                return Json::Value();
            }

            LOG_DEBUG("序列化结果：{}",body);
            cpr::Response rsp;
            try
            {
                rsp = _client->search(_index_name,_type,body);
                if(rsp.status_code < 200 || rsp.status_code >= 400)
                {
                    LOG_ERROR("查询数据失败：{}",rsp.status_code);
                    return Json::Value();
                }
            }
            catch(std::exception &e) 
            {
                LOG_ERROR("触发异常，查询失败");
                return Json::Value();
            }

            Json::Value res;
            if(UnSerialize(rsp.text,res) == false)
            {
                return Json::Value();
            }

            return res["hits"]["hits"];
        }

    private:
        std::string _index_name;
        std::string _type;
        ClientPtr _client; 
        Json::Value _must_not;
        Json::Value _should;
        Json::Value _must;
    };

}